package com.kamistoat.meimeistore.gateway.filter;

import com.kamistoat.meimeistore.commoncore.domain.R;
import com.kamistoat.meimeistore.commoncore.utils.ServletUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 黑名单过滤器.
 * AbstractGatewayFilterFactory<Config> 是gateway的自定义过滤器类，如果想要为gateway自定义过滤器，则必须继承该类。
 * 和全局过滤器GlobalFilter不同，自定义的过滤器默认不生效，必须在yaml文件中的route下使用断言，指定url，指定过滤器。
 * 在yaml文件中再详细介绍yaml断言。
 *
 * <Config>中可以定义过滤器使用到的各种属性/方法等，官方文档建议将过滤器使用到的所有属性和方法都定义在Config中
 */
@Component
public class BlackListUrlFilter extends AbstractGatewayFilterFactory<BlackListUrlFilter.Config> {

    /**
     * AbstractGatewayFilterFactory必须要实现的方法
     *
     * @param config 自定义过滤器的Config
     * @return
     */
    @Override
    public GatewayFilter apply(BlackListUrlFilter.Config config) {
        // GatewayFilter是一个接口，需要实现该接口的 filter 方法。
        GatewayFilter gatewayFilter = new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                // 获取拦截中请求的请求路径
                String url = exchange.getRequest().getURI().getPath();
                // 匹配黑名单
                if (config.matchBlacklist(url)) {
                    return ServletUtils.webFluxResponseWriter(exchange.getResponse(), HttpStatus.FORBIDDEN, "请求地址不允许访问", R.FAIL);
                }
                return chain.filter(exchange);
            }
        };
        return gatewayFilter;

        // 上面的写法也可以直接用一个lambda表达式完成
//        return (exchange, chain) -> {
//            String url = exchange.getRequest().getURI().getPath();
//            if (config.matchBlacklist(url)) {
//                return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求地址不允许访问");
//            }
//            return chain.filter(exchange);
//        };
    }

    /**
     * 也是自定义过滤器的固定写法，必须提供空构造器，并使用super直接链接其Config
     */
    public BlackListUrlFilter() {
        super(Config.class);
    }

    /**
     * BlackListUrlFilter会使用到的属性和方法。定义了显式黑名单列表和正则匹配列表
     */
    public static class Config {
        private List<String> blacklistUrl;
        private List<Pattern> blacklistUrlPattern = new ArrayList<>();

        public boolean matchBlacklist(String url) {
            return !blacklistUrlPattern.isEmpty() && blacklistUrlPattern.stream().anyMatch(p -> p.matcher(url).find());
        }

        public List<String> getBlacklistUrl() {
            return blacklistUrl;
        }

        public void setBlacklistUrl(List<String> blacklistUrl) {
            this.blacklistUrl = blacklistUrl;
            this.blacklistUrlPattern.clear();
            this.blacklistUrl.forEach(url -> {
                this.blacklistUrlPattern.add(Pattern.compile(url.replaceAll("\\*\\*", "(.*?)"), Pattern.CASE_INSENSITIVE));
            });
        }
    }
}
